很多細節都與前幾篇在實作 Account Service 一致,這邊算是一個複習。我建議讀者可以試著自己寫看看。接下來我還是會照著 Coding 的順序來實作,但解釋會變少,有不理解的可以回去重溫 Account Service 的流程。
開新的 protobuf 記得要加入專案內,並設定為 Server。
<ItemGroup>
<Protobuf Include="Protos\todoList.proto" GrpcServices="Server" />
</ItemGroup>
todoList.proto:
syntax = "proto3";
option csharp_namespace = "Todo.Grpc";
service TodoListGrpcService {
rpc CreateTodoList (CreateTodoListRequest) returns (TodoListResponse);
}
message CreateTodoListRequest {
string UserId = 1;
string Name = 2;
string Description = 3;
}
message TodoListResponse {
string Id = 1;
string UserId = 2;
string Name = 3;
string Description = 4;
string Status = 5;
}
Override gRPC 實作
using Grpc.Core;
using Todo.Application;
namespace Todo.Grpc.Services;
public class GrpcTodoListService : TodoListGrpcService.TodoListGrpcServiceBase
{
private readonly ITodoListService _todoListService;
public GrpcTodoListService(ITodoListService todoListService)
{
this._todoListService = todoListService;
}
public override Task<TodoListResponse> CreateTodoList(CreateTodoListRequest request, ServerCallContext context)
{
var result = _todoListService.CreateTodoList(new Guid(request.UserId), request.Name, request.Description);
TodoListResponse response = new();
return Task.FromResult(response);
}
}
namespace Todo.Application;
public interface ITodoListService
{
TodoListResult CreateTodoList(Guid userId, string name, string description);
}
using Todo.Domain.ValueObjects.Enums;
namespace Todo.Application;
public record TodoListResult(
Guid Id,
Guid UserId,
string Name,
string Description,
TodoListStatus Status
);
using Todo.Domain.Aggregates;
namespace Todo.Application;
public class TodoListService : ITodoListService
{
private readonly List<TodoList> _todoLists = new();
public TodoListResult CreateTodoList(Guid userId, string name, string description)
{
var list = TodoList.Create(name, description, userId);
_todoLists.Add(list);
return new TodoListResult(list.Id.Value, list.UserId, list.Name, list.Description, list.Status);
}
}
public override Task<TodoListResponse> CreateTodoList(CreateTodoListRequest request, ServerCallContext context)
{
var result = _todoListService.CreateTodoList(new Guid(request.UserId), request.Name, request.Description);
TodoListResponse response = new()
{
Id = result.Id.ToString(),
UserId = result.UserId.ToString(),
Name = result.Name,
Description = result.Description,
Status = result.Status.ToString()
};
return Task.FromResult(response);
}
using Microsoft.Extensions.DependencyInjection;
namespace Todo.Application;
public static class TodoApplicationRegister
{
public static IServiceCollection AddTodoApplication(this IServiceCollection services)
{
services.AddScoped<ITodoListService, TodoListService>();
return services;
}
}
using Todo.Grpc.Services;
using Todo.Application;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddGrpc();
builder.Services.AddGrpcReflection();
builder.Services.AddTodoApplication();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapGrpcService<GrpcTodoListService>();
app.MapGrpcReflectionService();
app.Run();
這邊記得把 Timeout 填長一點,避免之後得一直改。
我們就可以看到新增的 CreateTodoList
的 Endpoint。
測試一下,看來沒甚麼問題。
繼續來實作 TodoItem 的 Create 方法,跟先前的做法差不多,大家可以先自行練習後在閱讀。
開新的 protobuf 記得要加入專案內,並設定為 Server。
<ItemGroup>
<Protobuf Include="Protos\todoItem.proto" GrpcServices="Server" />
<Protobuf Include="Protos\todoList.proto" GrpcServices="Server" />
</ItemGroup>
todoItem.proto:
syntax = "proto3";
option csharp_namespace = "Todo.Grpc";
service TodoItemGrpcService {
rpc CreateTodoItem (CreateTodoItemRequest) returns (TodoItemResponse);
}
message CreateTodoItemRequest {
string ListId = 1;
string Content = 2;
}
message TodoItemResponse {
string Id = 1;
string ListId = 2;
string Content = 3;
string Status = 4;
string Color = 5;
}
Override gRPC 實作
using Grpc.Core;
using Todo.Application;
namespace Todo.Grpc.Services;
public class GrpcTodoItemService : TodoItemGrpcService.TodoItemGrpcServiceBase
{
private readonly ITodoItemService _todoItemService;
public GrpcTodoItemService(ITodoItemService todoItemService)
{
this._todoItemService = todoItemService;
}
public override Task<TodoItemResponse> CreateTodoItem(CreateTodoItemRequest request, ServerCallContext context)
{
var result = _todoItemService.CreateTodoItem(new Guid(request.ListId), request.Content);
TodoItemResponse response = new();
return Task.FromResult(response);
}
}
namespace Todo.Application;
public interface ITodoItemService
{
TodoItemResult CreateTodoItem(Guid listId, string content);
}
using Todo.Domain.ValueObjects;
namespace Todo.Application;
public record TodoItemResult(
Guid Id,
Guid ListId,
string Content,
TodoItemStatus Status
);
using Todo.Domain.Aggregates;
namespace Todo.Application;
public class TodoItemService : ITodoItemService
{
private readonly List<TodoItem> _todoItems = new();
public TodoItemResult CreateTodoItem(Guid userId, string content)
{
var item = TodoItem.Create(content, userId);
_todoItems.Add(item);
return new TodoItemResult(item.Id.Value, item.ListId, item.Content, item.Status);
}
}
public override Task<TodoItemResponse> CreateTodoItem(CreateTodoItemRequest request, ServerCallContext context)
{
var result = _todoItemService.CreateTodoItem(new Guid(request.ListId), request.Content);
TodoItemResponse response = new()
{
Id = result.Id.ToString(),
ListId = result.ListId.ToString(),
Content = result.Content,
Status = result.Status.State.ToString(),
Color = result.Status.Color
};
return Task.FromResult(response);
}
using Microsoft.Extensions.DependencyInjection;
namespace Todo.Application;
public static class TodoApplicationRegister
{
public static IServiceCollection AddTodoApplication(this IServiceCollection services)
{
services.AddScoped<ITodoListService, TodoListService>();
services.AddScoped<ITodoItemService, TodoItemService>();
return services;
}
}
app.MapGrpcService<GrpcTodoItemService>();
當 Map 一個新的 gRPC Service 後,Reflection 會讓 gRPC Clicker 知道有新的 Endpoint 和其 Payload。
測試一下,OK 沒有問題,有正確的 Status 和 Color。
附上目前為止的專案架構圖